package com.zxjbyte.yiyi.server.modifier;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.ImmutableSet;

import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static java.io.File.separator;

/**
 * 项目魔改器
 * 一键替换Maven的groupId、artifactId，项目的package等
 * @Author zhangxingjia
 * @Date 2023/3/26 22:12
 * @Version: 1.0
 */
public class ProjectModifier {

    // 是否需要详细输入日志
    private static final boolean isNeedDetailLog = true;

    // ========================old===========================
    public static final String groupId = "com.zxjbyte";
    /**
     *  这里artifactId定义两个是因为 笔者项目的根POM文件的artifactId是yiyi-boot
     *  其他子module的artifactId都是以yiyi-xxxx-xxxx开头的
     *  例如：yiyi-module-system
     *       yiyi-framework
     *       yiyi-starter-web
     */
    public static final String artifactId1 = "yiyi-boot";
    public static final String artifactId2 = "yiyi-";
    // 基础包名
    public static final String packageName = "com.zxjbyte.yiyi";
    // 类名中的关键字 例如 YiyiWebAutoConfiguration
    public static final String classKeyWords = "Yiyi";


    // ========================new===========================
    /**
     * 笔者的示例，例如你的公司域名为：com.baidu， 项目名字为crm项目
     * 对应笔者项目关系如下
     * com  -> com
     * yiyi -> baidu
     * boot -> crm
     */

    // 新的groupId
    public static final String newGroupId = "com.baidu";
    // 新的artifactId 1
    public static final String newArtifactId1 = "baidu-crm";
    // 新的artifactId 2
    public static final String newArtifactId2 = "baidu-";
    // 新的基础包名
    public static final String newPackageName = "com.baidu.crm";
    //一键改名后，“新”项目所在的目录
    public static final String newProjectDir = "baidu-crm";
    //新的类名中关键字
    public static final String newClassKeyWords = "Baidu";


    public static List<String[]> lastKeyWords(){
        List<String[]> list = new ArrayList<>();
        // ===================如不需要可注释掉====================
        list.add(new String[] {"yiyi", "baidu"});
        list.add(new String[] {"YIYI-BOOT", "BAIDU-CRM"});
        list.add(new String[] {"YIYI_BOOT", "BAIDU_CRM"});
        // ===================如不需要可注释掉====================
        return list;
    }

    /**
     * 白名单文件，不进行重写，避免出问题
     */
    private static final Set<String> WHITE_FILE_TYPES = ImmutableSet.of("gif", "jpg", "svg", "png", // 图片
            "eot", "woff2", "ttf", "woff", // 字体
            "mvnw", "mvnw.cmd", "lua"
            );

    /**
     * =======================================================================
     * ======如果此时代码看到这个位置，觉得不想配置这么多参数，欢迎使用一键魔改======
     * =========  {@link QuickProjectModifier }    =============
     * =======================================================================
     */
    public static void main(String[] args) {
        Date now = new Date();
        before(now);
        run();
        after(now);
    }

    public static void run(){
        String projectBaseDir = getProjectBaseDir();
        System.out.println("原项目路径地址 ["+ projectBaseDir +"]");
        String newProjectBaseDir = StrUtil.subBefore(projectBaseDir, separator, true) + separator + ModifierConstant.newProjectDir();
        System.out.println("新项目路径地址 ["+ newProjectBaseDir +"]");
        if (FileUtil.exist(newProjectBaseDir)) {
            System.out.println("新项目目录检测 ["+ newProjectBaseDir +"]已存在，请更改新的目录！程序退出");
            return;
        }
        // 如果新目录中存在 PACKAGE_NAME，ARTIFACT_ID 等关键字，路径会被替换，导致生成的文件不在预期目录
        if (StrUtil.containsAny(newProjectBaseDir, ModifierConstant.packageName(), ModifierConstant.artifactId1(), ModifierConstant.artifactId2())) {
            System.out.println("新项目目录检测 ["+ newProjectBaseDir +"] 存在冲突名称「"+ ModifierConstant.packageName() +"」或者「"+ ModifierConstant.artifactId1() +"」或者「"+ ModifierConstant.artifactId2() +"」，请更改新的目录！程序退出");
            return;
        }
        System.out.println("开始获得需要重写的文件，预计需要 10-20 秒");
        List<File> files = listFiles(projectBaseDir);
        if(CollUtil.isEmpty(files)){
            System.out.println("需要重写的文件数量为：0，程序退出");
            return;
        }else{
            System.out.println("需要重写的文件数量："+ files.size() +"，预计需要 15-30 秒");
        }
        for(File file : files){
            if(FileUtil.isDirectory(file) && FileUtil.isEmpty(file)){
                // 有且只为空目录时，直接创建
                mkdirDirectory(file, projectBaseDir, newProjectBaseDir);
                continue;
            }
            String fileType = FileUtil.extName(file);
            // 白名单中的文件直接替换
            if (WHITE_FILE_TYPES.contains(fileType)) {
                copyFile(file, projectBaseDir, newProjectBaseDir);
                continue;
            }
            // 内容替换写出文件
            String content = replaceFileContent(file);
            writeFile(file, content, projectBaseDir, newProjectBaseDir);
        }
        System.out.println("新项目所在地址为：" + newProjectBaseDir);
    }



    private static String replaceFileContent(File file) {
        String content = FileUtil.readUtf8String(file);
        String fileName = FileUtil.getName(file);
        if("ProjectModifier.java".equals(fileName) || "QuickProjectModifier.java".equals(fileName)
                || "ModifierConstant.java".equals(fileName)){
            content = StrUtil.replace(content, "package " + ModifierConstant.packageName(), "package " + ModifierConstant.newPackageName());
            return content;
        } else if("pom.xml".equals(fileName)){
            // 第一步重写pom文件 groupId
            content = StrUtil.replace(content, ModifierConstant.groupId(), ModifierConstant.newGroupId());
            // 第二步重新pom文件artifactId1
            content = StrUtil.replace(content, ModifierConstant.artifactId1(), ModifierConstant.newArtifactId1());
            content = StrUtil.replace(content, ModifierConstant.artifactId1().toUpperCase(), ModifierConstant.newArtifactId1().toUpperCase());
            // 第二步重新pom文件artifactId2
            content = StrUtil.replace(content, ModifierConstant.artifactId2(), ModifierConstant.newArtifactId2());
        } else {
            // 第一步重新包名
            content = StrUtil.replace(content, ModifierConstant.packageName(), ModifierConstant.newPackageName());
            // 第二步重写类名关键字
            content = StrUtil.replace(content, ModifierConstant.classKeyWords(), ModifierConstant.newClassKeyWords());
        }
        // 兜底内容中出现的注释，常量相关的替换
        if(CollUtil.isNotEmpty(ModifierConstant.getLastKeyWords())){
            for(String[] arr : ModifierConstant.getLastKeyWords()){
                content = StrUtil.replace(content, arr[0], arr[1]);
            }
        }
        return content;
    }

    private static void writeFile(File file, String fileContent, String projectBaseDir,
                                  String newProjectBaseDir) {
        String newPath = buildNewFilePath(file, projectBaseDir, newProjectBaseDir);
        FileUtil.writeUtf8String(fileContent, newPath);
    }

    private static void copyFile(File file, String projectBaseDir, String newProjectBaseDir) {
        String newPath = buildNewFilePath(file, projectBaseDir, newProjectBaseDir);
        FileUtil.copyFile(file, new File(newPath));
    }

    private static void mkdirDirectory(File file, String projectBaseDir, String newProjectBaseDir) {
        String newPath = buildNewFilePath(file, projectBaseDir, newProjectBaseDir);
        File newFileDir = new File(newPath);
        if(newFileDir.exists()){
            FileUtil.del(newFileDir);
        }
        FileUtil.mkdir(newFileDir);
    }

    private static String buildNewFilePath(File file, String projectBaseDir, String newProjectBaseDir) {
        // 文件的标准绝对路径
        String path = FileUtil.getAbsolutePath(file);
        // 第一步替换项目根目录
        path = StrUtil.replace(path, projectBaseDir, newProjectBaseDir);
        // 第二步包名替换
        String newPn = StrUtil.replace(ModifierConstant.newPackageName(), ".", separator);
        String pn = StrUtil.replace(ModifierConstant.packageName(), ".", separator);
        path = StrUtil.replace(path, pn, newPn);
        // 第三步ArtifactId替换
        path = StrUtil.replace(path, ModifierConstant.artifactId2(), ModifierConstant.newArtifactId2());
        // 第四步类名中关键字替换
        path = StrUtil.replace(path, ModifierConstant.classKeyWords(), ModifierConstant.newClassKeyWords());
        outDetailLog(path);
        return path;
    }

    private static String getProjectBaseDir() {
        // 当前目录的绝对路径
        String baseDir = System.getProperty("user.dir");
        if (StrUtil.isEmpty(baseDir)) {
            throw new NullPointerException("项目基础路径不存在");
        }
        return baseDir;
    }

    private static List<File> listFiles(String projectBaseDir) {
        List<File> files = FileUtil.loopFiles(projectBaseDir);
        // 移除 IDEA、Git 自身的文件
        files = files.stream()
                .filter(file -> !file.getPath().contains(separator + "target" + separator)
                        && !file.getPath().contains(separator + ".idea" + separator)
                        && !file.getPath().contains(separator + ".git" + separator)
                        && !file.getPath().contains(separator + "dist" + separator)
                        && !file.getPath().contains(separator + ".mvn" + separator)
                        && !file.getPath().contains(".iml")
                        && !file.getPath().contains(".flattened-pom.xml")
                        && !file.getPath().contains(".html.gz"))
                .collect(Collectors.toList());

        // 处理src下面已经建立好的空包，因空包笔者认为是项目结构，直接复制过去
        File file = FileUtil.file(projectBaseDir);
        if(file.isDirectory()){
            List<File> emptyDisList = new ArrayList<File>();
            emptyDisList = getEmptyDirs(file, emptyDisList);
            if(CollUtil.isNotEmpty(emptyDisList)){
                StringBuilder buf = new StringBuilder();
                buf.append("src").append(separator).append("main").append(separator).append("java").append(separator).append("com").append(separator)
                        .append("zxjbyte").append(separator).append("yiyi");
                for(File emptyDis : emptyDisList){
                    if(FileUtil.getAbsolutePath(emptyDis).contains(buf.toString())){
                        files.add(emptyDis);
                    }
                }
            }
        }
        return files;
    }

    private static List<File> getEmptyDirs(File directory, List<File> emptyDirs) {
        File[] filesAndDirs = directory.listFiles(); // 获取目录下的所有文件和子目录
        if (filesAndDirs != null) {
            for (File fileOrDir : filesAndDirs) {
                if (fileOrDir.isDirectory()) {
                    if(FileUtil.isEmpty(fileOrDir)){
                        // 如果是目录，递归处理子目录
                        emptyDirs.add(fileOrDir);
                    } else {
                        getEmptyDirs(fileOrDir, emptyDirs);
                    }
                }
            }
        }
        return emptyDirs;
    }

    private static void outDetailLog(Object obj){
        if(isNeedDetailLog){
            System.out.println("\33[32;4m😈魔改详情\33[0m \33[31;2m-->\33[0m " + Convert.toStr(obj));
        }
    }

    private static void after(Date now){
        System.out.println("魔改项目完成，共耗时："+ (DateUtil.spendMs(now.getTime()))  +" 毫秒");
    }

    private static void before(Date now){
        System.out.println("=============================================");
        System.out.println("======= 欢迎使用YIYI-BOOY项目魔改工具 =========");
        System.out.println("=============================================");
        System.out.println();
        System.out.println("项目魔改执行开始，当前时间 [" + DateUtil.formatDateTime(now) + "]");
        System.out.println();
    }
}
